import math
import random

from creep import Creep
from tower import Tower

from pathing import PathingAI

def creepgen(paths, map, game):
    return set(Creep(paths, spawn, game) for spawn in map.spawns)

def towergen(map, paths):
    towers = set()
    
    spawns = list(map.spawns)
    
    for i in range(20):
        l = random.randint(30, 70)
        
        place = random.choice(spawns)
        
        for n in range(l):
            place = paths[place]
        
        towers.add(Tower(place))
            
    return towers

class CreepIndex(object):
    def __init__(self, nodes):
        self.index = {node: set() for node in nodes}
    
    def __getitem__(self, key):
        return self.index[key]
    
    def __setitem__(self, key, value):
        self.index[key] = value

def reachability(towers, nodes):
    index = {tower: set() for tower in towers}
    
    for node in nodes:
        for tower in towers:
            x1, y1 = tower.position
            x2, y2 = node.position
            
            if math.hypot(x1 - x2, y1 - y2) <= tower.range:
                index[tower].add(node)
    
    return index
        
class Game(object):
    def __init__(self, generator):
        self.map = generator()
        
        self.pathing = PathingAI(self.map.graph, self.map.objective)
        self.pathing.calculate_paths()
        
        self.creepindex = CreepIndex(self.map.graph.nodes)
        
        self.creeps = creepgen(self.pathing.paths["test"], self.map, self)
        self.towers = towergen(self.map, self.pathing.paths["test"])
        
        self.towerindex = reachability(self.towers, self.map.graph.nodes)
        
        self.creepprev = {}
        
        for creep in self.creeps:
            self._creep_position(creep, None)
    
    def update(self):
        for unit in set(self.creeps):
            unit.update()
            if unit.next == unit.current:
                unit.active = False
                self.creeps.remove(unit)
         
        for tower in self.towers:
            if tower.target is None:
                #creeps = [creep for creep in self.creepindex[node] for node in self.towerindex[tower]]
                
                creeps = []
                
                for node in self.towerindex[tower]:
                    creeps.extend(self.creepindex[node])
                
                if creeps:
                    tower.target = creeps[0]
            elif tower.target.current not in self.towerindex[tower]:
                tower.target = None
            
            tower.update()
        
        #self.pathing.calculate_paths()
    
    def _creep_position(self, creep, prev):
        if creep in self.creepprev:
            self.creepindex[self.creepprev[creep]].discard(creep)
        self.creepindex[creep.next].add(creep)
        self.creepprev[creep] = creep.next
    
    def _remove_creep(self, creep):
        self.creepindex[self.creepprev[creep]].discard(creep)
